#ifndef __MY_WINDOW_SERVICE_H__
#define __MY_WINDOW_SERVICE_H__
#ifdef WIN32
#include "log/log.h"
#include <minwindef.h>
#include <ntdef.h>
#include <winnt.h>
#include <windef.h>
#include <winsvc.h>
#include <errhandlingapi.h>
#include <WinError.h>
#include <Winbase.h>
#include <Synchapi.h>

struct win_service_handle {
    char g_lpszServiceName[512];
    SERVICE_STATUS          ossecServiceStatus;
    SERVICE_STATUS_HANDLE   ossecServiceStatusHandle;
    int windows_ask_me_quit;
};
extern struct win_service_handle win_service_handle;

#define dump_win_error(fmt, ...) do { \
    char _local_msg[1025]; memset(_local_msg, 0, 1025); \
    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &_local_msg, sizeof(_local_msg), NULL); \
    LOGERR("%s "fmt"\n", _local_msg, ##__VA_ARGS__); \
} while(0)


void WINAPI OssecServiceStart (DWORD argc, LPTSTR *argv);
void wm_kill_children();
extern void stop_wmodules();
int UninstallService(const char *name);

static inline int _start_service(const char *name)
{
    int rc = -1;
    SC_HANDLE schSCManager, schService;
    schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (schSCManager) {
        schService = OpenService(schSCManager, name, SC_MANAGER_ALL_ACCESS);
        if (schService) {
            if (StartService(schService, 0, NULL)) {
                LOGINF("start ok\n");
                rc = 0;
            }
            if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING) {
                LOGINF("already started, ignore start request\n");
                rc = 0;
            } else {
                dump_win_error();
            }
            CloseServiceHandle(schService);
        } else {
            dump_win_error();
        }
        CloseServiceHandle(schSCManager);
    } else {
        dump_win_error();
    }
    return rc;
}
//
///* Stop OSSEC-HIDS service */
int os_stop_service()
{
    int rc = 0;
//    SC_HANDLE schSCManager, schService;
//
//    /* Stop the service database */
//    schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
//    if (schSCManager) {
//        schService = OpenService(schSCManager, win_service_handle.g_lpszServiceName,
//                                 SC_MANAGER_ALL_ACCESS);
//        if (schService) {
//            SERVICE_STATUS lpServiceStatus;
//
//            if (ControlService(schService, SERVICE_CONTROL_STOP, &lpServiceStatus)) {
//                rc = 1;
//            }
//
//            CloseServiceHandle(schService);
//        }
//
//        CloseServiceHandle(schSCManager);
//    }
//
//    /*
//    * Sleep for a short period of time to avoid possible race-conditions with
//    * newer instances of wazuh-agent.
//    */
//    Sleep(300); //milliseconds
//
    return (rc);
}
//
///* Check if the OSSEC-HIDS agent service is running
// * Returns 1 on success (running) or 0 if not running
// */
int CheckServiceRunning(const char *name)
{
    int rc = 0;
    SC_HANDLE schSCManager, schService;

    /* Check service status */
    schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (schSCManager) {
        schService = OpenService(schSCManager, name, SC_MANAGER_ALL_ACCESS);
        if (schService) {
            SERVICE_STATUS lpServiceStatus;
            if (QueryServiceStatus(schService, &lpServiceStatus)) {
                if (lpServiceStatus.dwCurrentState == SERVICE_RUNNING) {
                    rc = 1;
                }
            }
            CloseServiceHandle(schService);
        }

        CloseServiceHandle(schSCManager);
    }

    return (rc);
}

/* Install the OSSEC-HIDS agent service */
int InstallService(const char *path, const char *name, const char *desc)
{
    int ret;
    SC_HANDLE schSCManager, schService;
    LPCTSTR lpszBinaryPathName = NULL;
    SERVICE_DESCRIPTION sdBuf;

    /* Executable path -- it must be called with the full path */
    lpszBinaryPathName = path;

    schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (schSCManager == NULL) { goto install_error; }

    schService = CreateService(schSCManager,
                               name,
                               desc,
                               SERVICE_ALL_ACCESS,
                               SERVICE_WIN32_OWN_PROCESS,
                               SERVICE_AUTO_START,
                               SERVICE_ERROR_NORMAL,
                               lpszBinaryPathName,
                               NULL, NULL, NULL, NULL, NULL);

    if (schService == NULL) { CloseServiceHandle(schSCManager); goto install_error; }
    sdBuf.lpDescription = strdup(name);
    ret = ChangeServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, &sdBuf);
    CloseServiceHandle(schService);
    CloseServiceHandle(schSCManager);
    if (!ret) {
        goto install_error;
    }

    LOGINF("Successfully added to the service database.\n");
    return (1);
install_error:
    dump_win_error("Unable to create service");
    return (0);
}

//found and uninstall return 0
//not found           return 1
//fail                return -1
int UninstallService(const char *name)
{
    int ret = -1;
    SC_HANDLE schSCManager, schService;
    SERVICE_STATUS lpServiceStatus;

    schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (schSCManager == NULL) { dump_win_error("OpenSCManager error"); }
    if (schSCManager) {
        schService = OpenService(schSCManager, name, SERVICE_STOP | DELETE); if (schService == NULL) { LOGINF("Service does not exist (%s) nothing to remove.\n", name); ret = 1; }
        if (schService) {
            if (CheckServiceRunning(name)) {
                LOGINF("Found (%s) service is running going to try and stop it.\n", name);
                if (ControlService(schService, SERVICE_CONTROL_STOP, &lpServiceStatus)) {
                    LOGINF("Successfully stopped (%s).\n", name);
                } else {
                    dump_win_error("Failure stopping service (%s) before removing it", name);
                }
            } else {
                LOGINF("Found (%s) service is not running.\n", name);
            }

            if (DeleteService(schService)) {
                LOGINF("Successfully removed (%s) from the service database.\n", name); ret = 0;
            } else {
                dump_win_error("Failure removing (%s) from the service database.", name);
            }
            CloseServiceHandle(schService);
        }
        CloseServiceHandle(schSCManager);
    }

    return ret;
}

/* "Signal" handler */
VOID WINAPI OssecServiceCtrlHandler(DWORD dwOpcode)
{
    if (win_service_handle.ossecServiceStatusHandle) {
        switch (dwOpcode) {
            case SERVICE_CONTROL_STOP:
                win_service_handle.ossecServiceStatus.dwWin32ExitCode          = 0;
                win_service_handle.ossecServiceStatus.dwCheckPoint             = 0;
                win_service_handle.ossecServiceStatus.dwWaitHint               = 0;

                LOGINF("Received exit signal. Starting exit process.\n");
#ifdef OSSECHIDS
                win_service_handle.ossecServiceStatus.dwCurrentState           = SERVICE_STOP_PENDING;
                SetServiceStatus (win_service_handle.ossecServiceStatusHandle, &win_service_handle.ossecServiceStatus);
                LOGINF("Set pending exit signal.\n");

                wm_kill_children();
                stop_wmodules();
#endif
                win_service_handle.ossecServiceStatus.dwCurrentState           = SERVICE_STOPPED;
                SetServiceStatus (win_service_handle.ossecServiceStatusHandle, &win_service_handle.ossecServiceStatus);
                win_service_handle.windows_ask_me_quit = 1;
                break;
            default:
                LOGINF("win ask me %lu\n", dwOpcode);
                break;
        }
    }
}

/* Set the error code in the service */
void WinSetError()
{
    OssecServiceCtrlHandler(SERVICE_CONTROL_STOP);
}

void *wowow(void *arg)
{
    SERVICE_TABLE_ENTRY   steDispatchTable[] = {
        { "", OssecServiceStart },
        { NULL, NULL }
    };

    if (!StartServiceCtrlDispatcher(steDispatchTable)) {
        dump_win_error("");
        exit(1);
    }

    return NULL;
}

void WINAPI OssecServiceStart (__attribute__((unused)) DWORD argc, __attribute__((unused)) LPTSTR *argv)
{
    win_service_handle.ossecServiceStatus.dwServiceType            = SERVICE_WIN32;//_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
    win_service_handle.ossecServiceStatus.dwCurrentState           = SERVICE_START_PENDING;
    win_service_handle.ossecServiceStatus.dwControlsAccepted       = SERVICE_ACCEPT_STOP;
    win_service_handle.ossecServiceStatus.dwWin32ExitCode          = 0;
    win_service_handle.ossecServiceStatus.dwServiceSpecificExitCode = 0;
    win_service_handle.ossecServiceStatus.dwCheckPoint             = 0;
    win_service_handle.ossecServiceStatus.dwWaitHint               = 0;
    win_service_handle.ossecServiceStatusHandle = RegisterServiceCtrlHandler(win_service_handle.g_lpszServiceName, OssecServiceCtrlHandler);
    if (win_service_handle.ossecServiceStatusHandle == (SERVICE_STATUS_HANDLE)0) { LOGINF("RegisterServiceCtrlHandler failed.\n"); return; }
    win_service_handle.ossecServiceStatus.dwCurrentState = SERVICE_RUNNING;
    win_service_handle.ossecServiceStatus.dwCheckPoint = 0;
    win_service_handle.ossecServiceStatus.dwWaitHint = 0;
    if (!SetServiceStatus(win_service_handle.ossecServiceStatusHandle, &win_service_handle.ossecServiceStatus)) { LOGINF("SetServiceStatus error.\n"); return; }
}

int win_service_ctr(const char *service_exe_path, const char *name, const char *desc, const char *cmd)
{
    if (0) {
    } else if (strcmp(cmd, "install-service") == 0) {
	    InstallService(service_exe_path, name, desc);
	    return _start_service(name);
    } else if (strcmp(cmd, "uninstall-service") == 0) {
	    return (UninstallService(name));
    } else {
        LOGERR("Unknown cmd: %s\n", cmd);
    }
    return -1;
}

void win_service_init(void)
{
    pthread_t pid;
    pthread_create(&pid, NULL, wowow, NULL);
}
#endif /* WIN32 */
#endif
